/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_fpreduction.h                                                      *
 ****************************************************************************/

/**
 * @file elisa_fp_reduction.h
 * @brief Classes for propagation algorithms for CSP and "and model".
 */

#ifndef _ELISA_FP_REDUCTION_H
#define _ELISA_FP_REDUCTION_H

#include "elisa_defs.h"

#include "elisa_observer.h"

#include "elisa_reduction.h"
#include "elisa_compound_reduction.h"
#include "elisa_time.h"


NAMESPACE_ELISA
/**
 * @ingroup Propagation
 * @class EndCriterion
 * @brief Criterion used to stop propagation algorithms
 */
class EndCriterion
{
public:
	/// Constructor
	EndCriterion ()
	{
	}

	/// Destructor
	virtual ~EndCriterion ()
	{
	}

	/// Returns true if the end is detected
	virtual bool ok() const
	{
		return false;
	}
};

/**
 * @ingroup Propagation
 * @class EndPreciseEnough
 * @brief Criterion used to stop propagation algorithms
 * if no variable domain is splitable
 */
class EndPreciseEnough : public EndCriterion {
public:
	/**
 	 * Constructor
 	 * @param first SetVar iterator pointing to the first  variable of the model
 	 * @param last SetVar iterator pointing to the last  variable of the model
 	 */
	EndPreciseEnough (VarImplSet::iterator first,
	                  VarImplSet::iterator last)
	{
		_first = first;
		_last = last;
	}


	/**
 	 * Destructor
 	 */
	~EndPreciseEnough ()
	{
	}


	/**
 	 * Indicates if the end is reached
 	 * @return true if the end is detected
 	 */
	bool ok() const
	{
		VarImplSet::const_iterator it;
		for (it = _first; it != _last; ++it) {
			if ((*it)->isSplittable()) {
				return false;
			}
		}
		return true;
	}

private:
	VarImplSet::iterator _first, _last;
};

/**
 * @ingroup Propagation
 * @class FixedPointReduction
 * @brief Fixed-point propagation algorithm
 */
class FixedPointReduction : public CompoundReduction 
{
public:
	/**
 	 * Constructor
 	 * @param sc container for the ActiveReduction
 	 * @param end EndCriterion to detect the end
 	 */
	FixedPointReduction (BaseSortContainer *sc = 0, EndCriterion *end = 0)
	{
		if (sc == 0) {
			_activeReduction = new SortQueue;
		} else {
			_activeReduction = sc;
		}

		if (end == 0) {
			_end = new EndCriterion;
		} else {
			_end = end;
		}
	}

	/**
 	 * Destructor
 	 * @post both container and criterion are deleted
 	 */
	~FixedPointReduction ()
	{
		delete _activeReduction;
		delete _end;
	}

	/**
 	 * To active all reductions linked to this reduction
 	 */
	void initActive()
	{
		CompoundReduction::initActive();
		_activeReduction->clear();
		VecReduction::const_iterator it;
		for (it = _elemReduction.begin(); it != _elemReduction.end(); ++it) {
			_activeReduction->push(it->get());
		}
	}

	/**
 	 * To asleep all reductions linked to this reduction
 	 */
	void initInactive()
	{
		CompoundReduction::initInactive();
		_activeReduction->clear();
	}

	/**
 	 * Reduction function
 	 * @return true if the new domain is consistent (the reduction is ok)
 	 */
	bool reductionAlgorithm()
	{
		bool consistent = true;
		while (consistent
		       && (!_activeReduction->isEmpty())
		       && (!_end->ok())) {

			Reduction *o = static_cast<Reduction *>(_activeReduction->getTop());
			_activeReduction->pop();

			consistent = o->apply();
		}

		return consistent;
	}

	/**
 	 * Reduction o becomes active => added in the set of active reductions
 	 * @pre o have been active
 	 * @param o the reduction that must be  processed
 	 */
	void notifyActivation(Reduction *o)
	{
		if (o != 0) {
			_activeReduction->push(o);
		}
	}

	string getReductionName() const
	{
		return "FixedPoint";
	}

	virtual void print(std::ostream & os, std::string indent, VecConstraints *v) const
	{
		os << "FixedPointReduction ";
		CompoundReduction::print(os, indent, v);
	}

private:
	BaseSortContainer *_activeReduction; // set of active reductions
	EndCriterion *_end;           // end criterion
};

/**
 * @ingroup Propagation
 * @class SequenceReduction
 * @brief Implements a sequence of reductions
 */
class SequenceReduction : public CompoundReduction {
public:
	/**
 	 * Constructor
 	 */
	SequenceReduction ()
	{
	}

	/**
 	 * Destructor
 	 */
	~SequenceReduction ()
	{
	}

	/**
 	 * Reduction function
 	 * @return true if the new domain is consistent (the reduction is ok)
 	 */
	bool reductionAlgorithm()
	{
		std::cout << "SequenceReduction:: START" << std::endl;

		VecReduction::iterator it;
		for (it = _elemReduction.begin(); it != _elemReduction.end(); ++it) {
			if (!(*it)->apply()) {
 				return false;
			}
		}

		return true;
	}


	string getReductionName() const
	{
		return "Sequence";
	}

	virtual void print(std::ostream & os, std::string indent, VecConstraints *v) const
	{
		os << "SequenceReduction ";
		CompoundReduction::print(os, indent, v);
	}
};
END_NAMESPACE_ELISA
#endif //ELISA_FP_REDUCTION_H
